package com.example.framework.aop;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import com.example.common.annotation.OperLog;
import com.example.common.core.domain.entity.IpLocation;
import com.example.common.utils.IpUtils;
import com.example.system.dto.UserDTO;
import com.example.system.entity.OperationLog;
import com.example.system.entity.User;
import com.example.system.service.IOperationLogService;
import com.example.system.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author 王鹤蒲
 * @Date 2023 10 15
 */
@Component
@Aspect
@Slf4j
public class OperationLogAspect {

    @Autowired
    private IUserService iUserService;

    @Autowired
    private IOperationLogService iOperationLogService;


    @Around(value = "@annotation(com.example.common.annotation.OperLog)")
    public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        LocalDateTime operationTime = LocalDateTime.now();
        User loginUser = iUserService.getCurrentLoginUser();
        System.out.println(loginUser);
        Object[] args = proceedingJoinPoint.getArgs();
        if (loginUser == null) {
            if (args[0] instanceof UserDTO) {
                loginUser = new User();
                loginUser.setUserName(((UserDTO) args[0]).getUserName());
            }
        }
        if (loginUser == null) {
            log.error("记录日志信息错误，未能获取到当前操作用户信息");
            return null;
        }
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
        String operUrl = httpServletRequest.getRequestURI();
        Long startTime = System.currentTimeMillis();
        Object proceed = proceedingJoinPoint.proceed();
        Long endTime = System.currentTimeMillis();
        String executionTime = Long.toString(endTime - startTime)+"ms";
        MethodSignature method = (MethodSignature) proceedingJoinPoint.getSignature();
        Class<?> currentClass = proceedingJoinPoint.getTarget().getClass();
        String location = "内网IP";
        String ipAddr = IpUtils.getIpAddr(httpServletRequest);
        System.out.println(ipAddr);
        if (!IpUtils.internalIp(ipAddr)) {
            IpLocation ipLocation = IpUtils.getLocation(ipAddr);
            location = ipLocation.getCountry() + " " + ipLocation.getProvince() + ipLocation.getCity() + "(" + ipLocation.getIsp() + ")";
        }
        List<Object> allArgs =Arrays.asList(args).stream().map(arg -> {
            if (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)) {
                return arg;
            } else {
                return null;
            }
        }).filter(arg->arg!=null).collect(Collectors.toList());
        OperLog operLog = method.getMethod().getAnnotation(OperLog.class);
        RequestMapping requestMapping=method.getMethod().getAnnotation(RequestMapping.class);
        RequestMethod requestMethod = requestMapping.method()[0];
        OperationLog operationLog = OperationLog.builder()
                .operator(loginUser.getUserName())
                .operation(operLog.operation())
                .type(operLog.type().getValue())
                .requestMethod(requestMethod.name())
                .operUrl(operUrl)
                .ip(IpUtils.getIpAddr(httpServletRequest))
                .location(location)
                .method(currentClass.getName() + "." + method.getName())
                .operParam(JSONUtil.toJsonStr(allArgs))
                .jsonResult(JSONUtil.toJsonStr(proceed))
                .status((int) JSONUtil.parseObj(proceed).get("code") == 200 ? "1" : "0")
                .operationTime(operationTime)
                .executionTime(executionTime)
                .build();
        ThreadUtil.execAsync(() -> {
            iOperationLogService.save(operationLog);
        });
        return proceed;
    }

}
